"""
    @author    Thomas Lehmann
    @file      Table.py
    @language  Please use python >= 3.2

    @note Using the tool against itself should never throw any warning or error!
    @note I don't use TAB's and indentation is 4.
    @note I'm using the documentation tool 'epydoc' (http://epydoc.sourceforge.net/)
          for generating HTML documentation of this.
"""
__docformat__ = "javadoc en"

import unittest

class SimpleCell(object):
    """ represents a table cell. """
    def __init__(self, value = None):
        self.value = value

class SimpleColumn(object):
    """ represent a table column. """
    def __init__(self, title):
        """ storing the title only.
            @param title   is the column title
        """
        self.title = title

class SimpleRow(object):
    """ represent a table row. """
    def __init__(self, numberOfCells):
        """ prepares as many cells as required """
        self.cells = []

        for cell in range(numberOfCells):
            self.cells.append(SimpleCell())

    def setValueAt(self, value, column):
        """ sets a cell value.
            @param value    the value to write into the cell
            @param column   location of cell where the value has to be set.
            @return True when succeeded.
        """
        if column >= len(self.cells):
            return False

        self.cells[column].value = value
        return True

    def getValueAt(self, column):
        """ sets a cell value.
            @param column   location of cell where the value has to be set.
            @return None when failed or when there is no value set otherwise
                    the cell value will be returned.
        """
        if column >= len(self.cells):
            return None

        return self.cells[column].value

    def getWidth(self, column):
        """ calculates width of cell.
            @param column   location of cell for which the width is requested.
            @return -1 when failed otherwise the width of the cell.
        """
        if column >= len(self.cells):
            return -1

        return len("%s" % self.getValueAt(column))

class SimpleTable(object):
    def __init__(self):
        """ initializes containers for columns and rows (to be empty) """
        self.columns = []
        self.rows    = []

    def appendColumn(self, title):
        """ Appends a new column to the end. You have to defines all
            required columns before you append any row.
            @param title   the column title (has to be unique)
            @return -1 when failed otherwise the zero based index of last column added.
        """
        if not len(self.rows) == 0:
            return -1

        for column in self.columns:
            if column.title == title:
                return -1

        self.columns.append(SimpleColumn(title))
        return len(self.columns)-1

    def appendRow(self):
        """ Appends a new column to the end. You have to defines all
            required columns before you append any row.
            @return -1 when failed otherwise the index for last row added.
        """
        if len(self.columns) == 0:
            return -1

        self.rows.append(SimpleRow(len(self.columns)))
        return len(self.rows)-1

    def setValueAt(self, value, row, column):
        """ Sets a value for the defined cell.
            @param value   any value that can be represented as string.
            @param row     zero based index for the cell row.
            @param column  zero based index for the cell column.
            @return True, when succeeded.
        """
        if row >= len(self.rows):
            return False

        return self.rows[row].setValueAt(value, column)

    def getValueAt(self, row, column):
        """ Gets a value for the defined cell.
            @param row     zero based index for the cell row.
            @param column  zero based index for the cell column.
            @return None when failed otherwise the cell value.
        """
        if row >= len(self.rows):
            return None

        return self.rows[row].getValueAt(column)

    def getColumnWidth(self, column):
        """ Gets the maximum width of the given column
            @param column   zero based of the column
            @return -1 when failed otherwise the maximum width of the column
        """
        if column >= len(self.columns):
            return -1

        return max([row.getWidth(column) for row in self.rows])

    def dump(self):
        """ prints the table to stdout """
        # minimum width defined by the column titles
        widths = [len(column.title) for column in self.columns]
        # overwriting minimum widths when there are higher values
        for row in self.rows:
            for column in range(len(self.columns)):
                width = row.getWidth(column)
                if width > widths[column]:
                    widths[column] = width

        # writing column headers (left aligned)
        print("")
        rowText = "|"
        for column in range(len(self.columns)):
            rowText += " " + ("%%-%ds" % widths[column]) % self.columns[column].title + " |"
        print(rowText)

        # writing horizontal line as separator
        rowText = ""
        for column in range(len(self.columns)):
            rowText += " " + "-" * (widths[column]+2)
        print(rowText)

        # writing cell values
        for row in self.rows:
            rowText = "|"
            for column in range(len(self.columns)):
                value = row.getValueAt(column)
                if type(value) == type(0):
                    rowText += " " + ("%%%ds" % widths[column]) % value + " |"
                else:
                    rowText += " " + ("%%-%ds" % widths[column]) % value + " |"
            print(rowText)
        print("")

class SimpleTableTestCase(unittest.TestCase):
    """ testing of class SimpleTable """
    def testAppendOfColumns(self):
        """ testing of method SimpleTable.appendColumn """
        table = SimpleTable()
        # we don't have columns yet
        self.assertEqual(len(table.columns), 0)
        # we don't have rows yet
        self.assertEqual(len(table.rows), 0)

        # appending first column
        self.assertEqual(table.appendColumn("Filename"), 0)
        # you cannot append same column twice
        self.assertEqual(table.appendColumn("Filename"), -1)
        # appending second column
        self.assertEqual(table.appendColumn("LOC"), 1)

        # still we don't have rows yet
        self.assertEqual(len(table.rows), 0)

    def testAppendOfRows(self):
        """ testing of method SimpleTable.appendRow """
        table = SimpleTable()
        # you cannot append a row without defined columns
        self.assertEqual(table.appendRow(), -1)

        table.appendColumn("FileName")
        table.appendColumn("LOC")
        # appending first row
        self.assertEqual(table.appendRow(), 0)
        # appending second row
        self.assertEqual(table.appendRow(), 1)
        # you cannot append further columns when you have rows
        self.assertEqual(table.appendColumn("COM"), -1)

    def testSetGetValueAt(self):
        """ testing of methods SimpleTable.setValueAt/SimpleTable.getValueAt """
        table = SimpleTable()
        table.appendColumn("FileName")
        table.appendColumn("LOC")

        # you cannot set any value if there is no row
        self.assertFalse(table.setValueAt("SimpleTable.py", 0, 0))
        self.assertFalse(table.setValueAt(150, 0, 1))

        table.appendRow()
        # writing value into first cell
        self.assertTrue(table.setValueAt("SimpleTable.py", 0, 0))
        # writing value into second cell
        self.assertTrue(table.setValueAt(150, 0, 1))

        # reading value from first cell
        self.assertEqual(table.getValueAt(0, 0), "SimpleTable.py")
        # reading value from second cell
        self.assertEqual(table.getValueAt(0, 1), 150)

        # there's no third column
        self.assertEqual(table.getValueAt(0, 2), None)
        # there's no second row
        self.assertEqual(table.getValueAt(1, 1), None)

if __name__ == "__main__":
    suite = unittest.TestLoader().loadTestsFromTestCase(SimpleTableTestCase)
    unittest.TextTestRunner(verbosity=3).run(suite)

